ex12_01

StrBlob的data成员是一个指向string的vector的shared_ptr，因此StrBlob的赋值不会拷贝vector的内容，而是多个StrBlob对象共享一个（创建于动态内存空间上）的vector对象
代码第3行创建b2时提供了3个string的列表，因此会创建一个包含3个string的vector对象，并创建一个shared_ptr指向此对象（引用计数为1）
第4行将b2赋予b1时，创建一个shared_ptr也指向刚才创建的vector对象，引用计数变为2
因此，第4行向b2添加一个string时，会向两个StrBlob共享的vector中添加此string，最终，在代码结尾，b1和b2均包含4个string

ex12_03

push_back和pop_back的含义是向StrBlob对象共享的vector对象添加元素或从中删除元素，因此，不应该为其重载const版本，因为常量StrBlob对象不能够进行修改，也就不能够进行添加和删除操作

ex12_04

check函数是一个私有成员函数，即它只被StrBlob的成员函数调用，不会被用户程序所调用，又因为data_size的成员类型是size_type,这是一个无符号类型，不会产生负数，因此在定义的check函数中，不需要检查i是否大于0

ex12_05

explicit的作用是阻止构造函数进行的隐式转换，所以如果没有编写一个initializer_list explicit，那么StrBlob可以进行隐式转换，即在需要StrBlob的地方可以使用列表进行替代，而且，可以进行拷贝形式的初始化，这令编写程序更为简单
但是这种隐式转换并不总是好的，因为列表中可能并非都是合法的值，另一方面，对于接受StrBlob的函数，传递给它一个列表，会创建一个临时的StrBlob对象，对其进行列表初始化，，然后将其传递给函数，当函数完成后，此对象被丢弃，再也无法访问了，对于这些情况，我们可以定义显式的构造函数，禁止隐式类类型转换

ex12_08

按照这段程序执行的话，p将会被转变成一个bool类型的值，这意味着一个动态内存在分配后无法释放了，会造成内存泄漏

ex12_09

这段代码很好的展示了智能指针在管理内存上的优点：
对于普通指针部分，首先分配了两个int型对象，指针分别保存在p和r中，接下来，将指针q的值赋予了r，这会带来两个严重的问题：
  1.首先是一个直接的内存泄漏问题，r和q一样都指向42的内存地址，而r中原来保存的地址--100的内存没有指针来进行管理了，变成了“孤儿内存”，从而造成了内存泄漏；
  2.其次是一个“空悬指针”的问题，由于r和q指向同一个动态对象，如果程序编写不当，会产生释放了其中一个指针，而继续使用另一个指针的问题，继续使用的指针指向的是一块已经释放的内存，是一个空悬指针，继续读写它指向的内存可能导致程序崩溃甚至系统崩溃的严重问题。
  使用shared_ptr则可以很好地解决这些问题，首先，分配了两个共享的对象，分别由共享指针p2和q2指向，因此它们的引用计数都是1，接下来，将q2赋值给r2，赋值操作会将q2指向的对象地址赋予r2，这样r2被赋予了新值，因此r2的引用计数减1，将q2拷贝给r2，因此q2的的引用计数加1，这样，前者的引用计数变为0，占用的内存空间就会被释放，不会造成内存的泄漏，而后者的引用计数变为2，也不会因为r2和q2之一的销毁就释放它的内存空间，因此也不会造成空悬指针的问题
  
ex12_10

这个调用是正确的，首先用p创建了一个临时的智能指针shared_ptr,p和shared_ptr都指向相同的int对象，引用计数被置为2，process执行完毕后，ptr被销毁，引用计数减去1，变成了1，这是正确的，只有p指向它

ex12_11

就像书中说的那样，普通指针和智能指针是不能混合使用的，p.get()得到的是一个普通内置指针，一旦调用结束，该临时指针被销毁，p就变成了空悬指针，意味着当再次使用p的时候，p是未定义的

ex12_12

(a)是合法的，sp是一个共享的指针，指向一个int对象，对process的调用会拷贝sp，传递给process的参数ptr，两者都指向相同的int，引用计数变为2，当process执行完毕时，ptr被销毁，引用计数变回1
(b)是合法的，new创建了一个int对象，指向它的指针被用来创建了一个shared_ptr，传递给process的参数ptr，引用计数为1，当process执行完毕，ptr被销毁，引用计数变为0，临时int对象被销毁，不存在内存泄漏和空悬指针的问题
(c)不合法，因为不能把普通的内置指针转换为智能指针shared_ptr<int>
(d)是合法的，但是这是一段有问题的程序：p是一个指向int对象的普通指针，被用来创建一个临时的智能指针shared_ptr,传递给process的参数ptr，引用计数是1，当process执行完毕后，ptr被销毁，引用计数变为0，int对象被销毁，释放内存，那么p就成了空悬指针

ex12_13

第2行用get获取了sp指向的int对象的地址，第3行用delete删除了p的地址，所以int对象被释放了，sp称为一个空悬指针的shared_ptr


ex12_17

(a)是错误的，根据代码片段可以知道ix是一个int类型的数据，unique_ptr需要用到一个指针对其进行初始化，无法将int类型转换为指针类型，因此(a)是错误的；
(b)是正确的，pi是一个int型的指针，可以用来初始化unique_ptr，但是这个程序逻辑上是错误的，因为它用一个普通int变量的地址初始化p1，p1销毁时会释放这个内存，其行为是未定义的；
(c)是合法的，因为new可以用来动态分配和初始化对象，因此pi2就是一个动态分配的对象的指针，用其来初始化unique_ptr是正确的；
(d)是合法的，但是和(b)存在一样的问题；
(e)是合法的，理由和（c）类似
(f)是合法的，但是用p2管理的对象的地址来初始化p5，造成两个unique_ptr指向相同的内存地址，当其中一个unique_ptr被销毁的时候，该内存被释放，另一个unique_ptr变成了空悬指针

ex12_18

release操作是用来将指针的所有权从一个unique_ptr转移到另一个unique_ptr，而多个shared_ptr可以共享对象的所有权，可以进行拷贝和赋值，因此不需要release来转移所有权


ex12_21

这两段代码的功能是完全一样的，但是我认为书中的代码会更好一些，因为书中的代码将合法性检查与元素获取和返回语句分考了，代码更清晰易懂，当执行到第二条语句时，已经能够保证p是存在的vector，curr是合法的位置，可以安全的获取元素并返回，这种清晰的结构有利于修改不同的处理逻辑。
本题中的版本将合法性检查和元素获取及返回合在一条语句中，不容易读取，也不容易修改

ex12_22

本题中不能将StrBlobPtr绑定到一个const StrBlob是因为构造函数接受一个非const StrBlob对象的引用，所以如果想让StrBlobPtr使用const StrBlob，那么应该让构造函数接受一个const StrBlob对象的引用，并为StrBlob定义能操作const对象的begin和end：
声明：
StrBlobPtr begin() const;
SttrBlobPtr end() const;
定义：
inline StrBlobPtr StrBlob::begin() const
{
  return StrBlobPtr(*this);
}
inline StrBlobPtr StrBlob::end() const
{
  auto ret=StrBlobPtr(*this,data->size());
  return ret;
}

ex12_25

释放pa应用一种特殊形式的delete，即delete[] pa;


ex12_31

vector是更好的选择，因为vector和set的区别在于set中的元素是自然排序的，但是本题中，因为我们是逐行输入文本，所以每个单词出现的行号自然是按照升序排列的，根据时间复杂度来看，set的insert时间复杂度是o(logn),vector的时间复杂度是n，所以用vector来说是更好的选择

ex12_33

ResultIter begin() const { return nos->begin(); }
ResultIter end() const { return nos->end(); }
shared_ptr<StrBlob> get_file() const { return input; }
